package cn.le.io;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.Arrays;

/**
 * 子类:heapByteBuffer,DirectByteBuffer
 *
 * DirectByteBuffer不是分配在堆上面的,它不被Gc直接管理(但DirectByteBuffer的Java对象是归gc管理的,只要Gc回收了它的
 * Java对象,操作系统才会释放Direct buffer所申请的空间),它似乎给人的感觉是"内核缓冲区(buffer in kernel)"
 * HeapByteBuffer测试分配在堆上面的,或者我们可以简单理解为heap buffer就是byte[]数组的一种封装,查看Java源码,heapbytebuffer
 * 也确实是这样,说白了就是heapByteBuffer是在jvm堆内存中分配会被jvm管理回收,但是directByteBuffer是直接由系统内存进行分配,
 * 不被jvm管理.
 *
 * 区别:
 *  创建是释放DirectByteBuffer的代价比HeapByteBuffer高,因为jvm堆中分配和释放内存肯定比系统分配和创建内存高效
 *  因为平时的read和writer,都会在io设备与应用程序空间经历一个"内核缓冲区". DirectByteBuffer就是好比是"内核缓冲区"上
 *  的缓存,不直接接受gc管理,而heapbuffer就是仅仅在byte[]字节数组的包装,因此把一个Directbuffer写入一个channel的速度要比
 *  把一个HeapBytebuffer写入一个channel的速度要快
 *
 * 一般如果是bytebuffer经常被重用的话,就可以使用DirectByteBuffer对象,如果是需要经常释放和分配的地方用HeapByteBuffer对象
 *
 * mark<=position<=limit<=capacity
 *
 *
 */



public class ByteBufferDemo {

    public static void main(String[] args) {

        //ByteBufferDemo.viewBuffer();
    }

    /**
     * 视图缓冲区可以让我们通过某个特定的基本数据类型的视窗查看底层的ByteBuffer,ByteBuffer依然是实际存储数据的地方,
     * 支出着前面的视图,因此,对视图的任何修改都会映射成对ByteBuffer中数据的修改,视图还允许我们冲bytebuffer一次一个地
     * (一bytebuffer所支持的方式相同)或者成批(放入数组中)读取基本类型值
     */
    public static void viewBuffer()
    {
        ByteBuffer bb = ByteBuffer.allocate(100);
        IntBuffer ib = bb.asIntBuffer();
        ib.put(new int[]{11,42,47,99,143});
        ib.put(1,33);
        ib.flip();
        while (ib.hasRemaining())
        {
            int i = ib.get();
            System.out.println(i);
        }
        System.out.println("-------------------");
        bb.flip();
        while (bb.hasRemaining())
        {
            System.out.println(bb.get());
        }

        /*rewind()方法,设置状态,准备读取数据,会吧position设置为0,
        *但不会把limit设置为position,limit还是等于capacity,所以会导致读出脏数据
        *
        * 查看bytebuffer容量剩余的方法remaining()和hasRemaining()
        * hasRamaining()方法是判断bytebuffer有没有到上限,即position是否大于limit
        * remaining()方法获取当前剩余的范围值,即limit-position
        *
        * wrap()直接将byte数组设置到bytebuffer内部的一个全局byte数组,所以这个byte数据和
        * bytebuffer内容是共享的,相当于allocate+put操作,所以它的内存是分配在堆上面的,写的数据和
        * bytebuffer内容在堆中是一份数据,相互影响,所有这个方法在使用的时候需要特别小心,注意数据的有
        * 效性
        */
    }

    /**
     * 子buffer操作
     *
     * slice方法获取的是源bytebuffer的position-limit之间的内容,和源内容相互影响,源内容的position和limit不受影响
     * duplicate方法获取的是源bytebuffer所有的内容,包括远bytebuffer的Mark,position,limit,
     *  capacity,和源内容相互影响,源内容的position和limit不受影响
     * array方法获取的是源bytebuffer的所有内容,只是存放到一个字节数组中,和源内容相互影响,源position和limit不受影响
     * get方法获取是源bytebuffer的position和limit之间的内容,存放到目标字节数组中,和源内容不影响
     *  源position会发生变化等于limit,limit不受影响
     */

    /**
     * 数据压缩和其他基本数据之间的转化
     *
     * compact就是压缩的意思,position之间的数据认为是无效的,就把position到limit之间的数据copy到起始位置
     *  大小为limit-position,拷贝完成后position=limit-position;limit=capacity
     *
     * order方法是设置bytebuffer的字节顺序的,Java中默认的是大端顺序,但是有时候我们想根据本机系统的字节
     *  顺序,这个时候可以直接使用nativeOrder方法来设置,只有设置好了资金顺序,才能进行后续的其他类型的值
     *  的获取,比如从bytebuffer中获取一个int类型的值getint,但是这个有一个主意的地方,就是一个int是四个字节
     *  那么当取到剩余的数据不足四个字节的时候,就会出错,所以需要我们做一些检查,通过getIntBuffer方法获取一个intbuffer内容,
     *  获取之后就可以修改int类型的值,而这个修改会影响到之前bytebuffer内容.
     *
     * byteOrder big_endian:(高位优先)字节大头在前排序(将最重要的字节存放在地址最低的存储器单元),也是Java中的默认字节排序
     * byteOrder little_endian:(地位优先)字节大头在后面排序(将最重要的字节存放在地址最高的存储器单元),也是系统默认的字节排序,byteOrder.nativeOrder()
     *  这个方法是直接获取当前系统的字节是那种排序的
     */

    public static void endians()
    {

        ByteBuffer bb = ByteBuffer.wrap(new byte[12]);
        //通过CharBuffer视图可以将chararray插入到bytebuffer中
        bb.asCharBuffer().put("abcdef");
        System.out.println(Arrays.toString(bb.array()));
        bb.rewind();
        bb.order(ByteOrder.BIG_ENDIAN);
        bb.asCharBuffer().put("abcdef");
        System.out.println(Arrays.toString(bb.array()));
        bb.rewind();
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.asCharBuffer().put("abcdef");
        System.out.println(Arrays.toString(bb.array()));

    }

}
























